#ifndef _GRO_BEACONS_H__
#define _GRO_BEACONS_H__

#include <string>
#include <iostream>
#include <memory>
using namespace std;
#include <set>


namespace GRO
{
    using std::string;
    using std::set;
    
// A class that holds a variable or a pointer to a variable.
// Abstracts access to such data
    template<class T>
    class LinkVar
    {
    private:
        T _value;
        T* _link;

    public:
        LinkVar()
                : _value(T())
                , _link(NULL)
        {}
        LinkVar(const T& value)
                : _value(value)
                , _link(NULL)
        {}
        LinkVar(T* value_ptr)
                : _value(T())
                , _link(value_ptr)
        {}
        void link(T* value_ptr)
        {
            _link = value_ptr;
        }
        T& get()
        {
            if (_link == NULL)
                return _value;
            return *_link;
        }
        const T& get() const
        {
            if (_link == NULL)
                return _value;
            return *_link;
        }
        void set(const T& value)
        {
            if (_link == NULL)
                _value = value;
            else
                *_link = value;
        }
    };
    
    
    class Beacon;
    class Connection;
    class Observer;
    
    // Observer
    
    class Observer
    {
    public:
        set<Connection*> GRO_connections;
    
        Observer()
        {}
        
        void connect(Connection* c);
        
        void disconnect(Connection* c);
    };
    
    
    

// ***********************************
// *********** Connections ***********
// ***********************************

    class BeaconObj;

    class Connection
    {
    public:
        weak_ptr<BeaconObj> beacon;
        Observer* observer;
    
        int id;
        int priority;
        bool stop;  // Causes beacon to stop after processing this connection
        Connection();
        Connection(const Connection& connection);
        virtual ~Connection();
        Connection& operator=(const Connection& connection);
        
        void disconnect();
        void disconnect(Beacon* b);
        
        // This should be virtual because only MethodConnections really have Observers
        void disconnect(Observer* o);
        
        bool isConnected()
        {
            return (!beacon.expired());// && observer != NULL);  // If only MethodConnections have Observers, then this was wrong.
        }
        
        virtual void trigger() = 0;
        virtual Connection* clone() const = 0;
    };
    
    
    class ScopedConnection
    {
    public:
        Connection* connection;
    
        ScopedConnection()
            : connection(NULL)
        {}
        ScopedConnection(ScopedConnection const& sc)
            : connection(sc.connection)
        {}
        ScopedConnection(Connection* c)
            : connection(c)
        {}
        
        ScopedConnection& operator=(Connection* c)
        {
            connection = c;
            return *this;
        }
        
        ~ScopedConnection()
        {
            delete connection;
        }
    };
    
    class ScopedConnections
    {
    public:
        set<Connection*> connections;
    
        ScopedConnections()
        {}
        ScopedConnections(ScopedConnections const& scs)
            : connections(scs.connections)
        {}
        ScopedConnections(Connection* c)
        {
            connections.insert(c);
        }
        
        ScopedConnections& operator+=(Connection* c)
        {
            connections.insert(c);
            return *this;
        }
        
        ~ScopedConnections()
        {
            for(set<Connection*>::iterator e = connections.begin(); e != connections.end(); e++)
                delete (*e);
        }
    };
    
    
    
    
    
    class CompareConnections {
        public:
        bool operator() (const shared_ptr<Connection>& lhs, const shared_ptr<Connection>& rhs) const
          //{return lhs->id > rhs->id;}
          {return lhs->priority > rhs->priority;}
    };


    // Intermediate derived class to make a Collector interface possible.
    template<class RETURN_TYPE>
    class ConnectionType : public Connection
    {
    protected:
        LinkVar<RETURN_TYPE> value;
    public:
        ConnectionType()
                : Connection()
        {}
        virtual ~ConnectionType()
        {}
        void setValue(const RETURN_TYPE& Value)
        {
            value.set(Value);
        }
        void linkValue(RETURN_TYPE* value_ptr)
        {
            value.link(value_ptr);
        }
        RETURN_TYPE& getValue()
        {
            return value.get();
        }
        virtual Connection* clone() const = 0;
    };

    template<>
    class ConnectionType<void> : public Connection
    {
    public:
        ConnectionType()
                : Connection()
        {}
        virtual ~ConnectionType()
        {}
        virtual Connection* clone() const = 0;
    };






// Connection with 0 arguments

    template<class RETURN_TYPE>
    class Connection0 : public ConnectionType<RETURN_TYPE>
    {
    private:
        RETURN_TYPE (*_fn)();

    public:
        Connection0()
                : ConnectionType<RETURN_TYPE>()
        {}
        Connection0(RETURN_TYPE (*FN)())
                : ConnectionType<RETURN_TYPE>()
        {
            connect(FN);
        }
        virtual ~Connection0()
        {}

        void connect(RETURN_TYPE (*FN)())
        {
            _fn = FN;
        }

        void trigger()
        {
            if(_fn != NULL)
                ConnectionType<RETURN_TYPE>::value.set(_fn());
        }
        
        virtual Connection* clone() const
        {
            return new Connection0(*this);
        }
    };

// Specialization of Connection0 for void
    template<>
    class Connection0 <void> : public ConnectionType<void>
    {
    private:
        void (*_fn)();

    public:
        Connection0()
                : ConnectionType<void>()
        {}
        Connection0(void (*FN)())
                : ConnectionType<void>()
        {
            connect(FN);
        }
        virtual ~Connection0()
        {}

        void connect(void (*FN)())
        {
            _fn = FN;
        }

        void trigger()
        {
            if(_fn != NULL)
                _fn();
        }
        
        virtual Connection* clone() const
        {
            return new Connection0(*this);
        }
    };
    
// Connection for methods with 0 arguments

    template<class CLASS, class RETURN_TYPE>
    class MethodConnection0 : public ConnectionType<RETURN_TYPE>
    {
    private:
        
        CLASS* object;
        RETURN_TYPE (CLASS::*method)();
        
        RETURN_TYPE call()
        {
            if(object != NULL)
                return (object->*method)();
            return RETURN_TYPE();  // FIXME: This should actually be an exception
        }

    public:
        MethodConnection0()
                : ConnectionType<RETURN_TYPE>()
        {}
        MethodConnection0(CLASS* object, RETURN_TYPE (CLASS::*method)())
                : ConnectionType<RETURN_TYPE>()
        {
            connect(object, method);
        }
        virtual ~MethodConnection0()
        {}

        void connect(CLASS* object, RETURN_TYPE (CLASS::*method)())
        {
            this->object = object;
            this->method = method;
        }
        void trigger()
        {
            ConnectionType<RETURN_TYPE>::value.set(call());
        }
        
        virtual Connection* clone() const
        {
            return new MethodConnection0(*this);
        }
    };

// Specialization of MethodConnection0 for void

    template<class CLASS>
    class MethodConnection0<CLASS, void> : public ConnectionType<void>
    {
    private:
        CLASS* object;
        void (CLASS::*method)();
         

    public:
        MethodConnection0()
                : ConnectionType<void>()
        {}
        MethodConnection0(CLASS* object, void (CLASS::*method)())
                : ConnectionType<void>()
        {
            connect(object, method);
        }
        virtual ~MethodConnection0()
        {}

        void connect(CLASS* object, void (CLASS::*method)())
        {
            this->object = object;
            this->method = method;
        }
        void trigger()
        {
            call();
        }
        
        void call()
        {
            if(object != NULL)
                (object->*method)();
        }
        
        virtual Connection* clone() const
        {
            return new MethodConnection0(*this);
        }
    };
    
    

// Connection with 1 argument

    template<class RETURN_TYPE, class ARG1_TYPE>
    class Connection1 : public ConnectionType<RETURN_TYPE>
    {
    private:
        RETURN_TYPE (*_fn)(ARG1_TYPE);
        LinkVar<ARG1_TYPE> arg1;

    public:
        Connection1()
                : ConnectionType<RETURN_TYPE>()
        {}
        Connection1(RETURN_TYPE (*FN)(ARG1_TYPE), const LinkVar<ARG1_TYPE>& arg1)
                : ConnectionType<RETURN_TYPE>()
                , arg1(arg1)
        {
            connect(FN);
        }
        virtual ~Connection1()
        {}

        void connect(RETURN_TYPE (*FN)(ARG1_TYPE))
        {
            _fn = FN;
        }

        void trigger()
        {
            if(_fn != NULL)
                ConnectionType<RETURN_TYPE>::value.set(_fn(arg1.get()));
        }

        void setArg1(const ARG1_TYPE& value)
        {
            arg1.set(value);
        }
        void linkArg1(ARG1_TYPE* value_ptr)
        {
            arg1.link(value_ptr);
        }
        ARG1_TYPE getArg1()
        {
            return arg1.get();
        }
        
        virtual Connection* clone() const
        {
            return new Connection1(*this);
        }
    };

// Specialization of Connection1 for void
    template<class ARG1_TYPE>
    class Connection1<void, ARG1_TYPE> : public ConnectionType<void>
    {
    private:
        void (*_fn)(ARG1_TYPE);
        LinkVar<ARG1_TYPE> arg1;

    public:
        Connection1()
                : ConnectionType<void>()
        {}
        Connection1(void (*FN)(ARG1_TYPE), const LinkVar<ARG1_TYPE>& arg1)
                : ConnectionType<void>()
                , arg1(arg1)
        {
            connect(FN);
        }
        virtual ~Connection1()
        {}

        void connect(void (*FN)(ARG1_TYPE))
        {
            _fn = FN;
        }

        void trigger()
        {
            if(_fn != NULL)
                _fn(arg1.get());
        }

        void setArg1(const ARG1_TYPE& value)
        {
            arg1.set(value);
        }
        void linkArg1(ARG1_TYPE* value_ptr)
        {
            arg1.link(value_ptr);
        }
        ARG1_TYPE getArg1()
        {
            return arg1.get();
        }
        
        virtual Connection* clone() const
        {
            return new Connection1(*this);
        }
    };
    
    
// Connection for methods with 1 argument

    template<class CLASS, class RETURN_TYPE, class ARG1_TYPE>
    class MethodConnection1 : public ConnectionType<RETURN_TYPE>
    {
    private:
        CLASS* object;
        RETURN_TYPE (CLASS::*method)(ARG1_TYPE);
        
        LinkVar<ARG1_TYPE> arg1;

    public:
        MethodConnection1()
                : ConnectionType<RETURN_TYPE>()
        {}
        MethodConnection1(CLASS* object, RETURN_TYPE (CLASS::*method)(ARG1_TYPE), const LinkVar<ARG1_TYPE>& arg1)
                : ConnectionType<RETURN_TYPE>()
                , arg1(arg1)
        {
            connect(object, method);
        }
        virtual ~MethodConnection1()
        {}
        
        void connect(CLASS* object, RETURN_TYPE (CLASS::*method)(ARG1_TYPE))
        {
            this->object = object;
            this->method = method;
        }
        
        void trigger()
        {
            ConnectionType<RETURN_TYPE>::value.set(call(arg1.get()));
        }
            
        RETURN_TYPE call(const ARG1_TYPE& arg1)
        {
            if(object != NULL)
                return (object->*method)(arg1);
            return RETURN_TYPE();  // FIXME: This should actually be an exception
        }
        
        virtual Connection* clone() const
        {
            return new MethodConnection1(*this);
        }
    };
    
// Specialization of MethodConnection1 for void

    template<class CLASS, class ARG1_TYPE>
    class MethodConnection1 <CLASS, void, ARG1_TYPE> : public ConnectionType<void>
    {
    private:
        CLASS* object;
        void (CLASS::*method)(ARG1_TYPE);
        LinkVar<ARG1_TYPE> arg1;

    public:
        MethodConnection1()
                : ConnectionType<void>()
                , object(NULL)
                , method(NULL)
        {}
        MethodConnection1(CLASS* object, void (CLASS::*method)(ARG1_TYPE), const LinkVar<ARG1_TYPE>& arg1)
                : ConnectionType<void>()
                , object(object)
                , method(method)
                , arg1(arg1)
        {}
        virtual ~MethodConnection1()
        {}
        
        void connect(CLASS* object, void (CLASS::*method)(ARG1_TYPE))
        {
            this->object = object;
            this->method = method;
        }

        void trigger()
        {
            call(arg1.get());
        }
            
        void call(const ARG1_TYPE& arg1)
        {
            if(object != NULL)
                (object->*method)(arg1);
        }
        
        virtual Connection* clone() const
        {
            return new MethodConnection1(*this);
        }
    };


// Connection with 2 arguments

    template<class RETURN_TYPE, class ARG1_TYPE, class ARG2_TYPE>
    class Connection2 : public ConnectionType<RETURN_TYPE>
    {
    private:
        RETURN_TYPE (*_fn)(ARG1_TYPE, ARG2_TYPE);
        LinkVar<ARG1_TYPE> arg1;
        LinkVar<ARG2_TYPE> arg2;

    public:
        Connection2()
                : ConnectionType<RETURN_TYPE>()
                , _fn(NULL)
        {}
        Connection2(RETURN_TYPE (*FN)(ARG1_TYPE, ARG2_TYPE), const LinkVar<ARG1_TYPE>& arg1, const LinkVar<ARG2_TYPE>& arg2)
                : ConnectionType<RETURN_TYPE>()
                , _fn(FN)
                , arg1(arg1)
                , arg2(arg2)
        {}
        virtual ~Connection2()
        {}

        void connect(RETURN_TYPE (*FN)(ARG1_TYPE, ARG2_TYPE))
        {
            _fn = FN;
        }

        void setArg1(const ARG1_TYPE& value)
        {
            arg1.set(value);
        }
        void linkArg1(ARG1_TYPE* value_ptr)
        {
            arg1.link(value_ptr);
        }
        ARG1_TYPE getArg1()
        {
            return arg1.get();
        }
        void setArg2(const ARG2_TYPE& value)
        {
            arg2.set(value);
        }
        void linkArg2(ARG2_TYPE* value_ptr)
        {
            arg2.link(value_ptr);
        }
        ARG2_TYPE getArg2()
        {
            return arg2.get();
        }
        
        void setArgs(const ARG1_TYPE& val1, const ARG2_TYPE& val2)
        {
            arg1.set(val1);
            arg2.set(val2);
        }
        void setArgs(ARG1_TYPE* val1, const ARG2_TYPE& val2)
        {
            arg1.link(val1);
            arg2.set(val2);
        }
        void setArgs(const ARG1_TYPE& val1, ARG2_TYPE* val2)
        {
            arg1.set(val1);
            arg2.link(val2);
        }
        void setArgs(ARG1_TYPE* val1, ARG2_TYPE* val2)
        {
            arg1.link(val1);
            arg2.link(val2);
        }

        void trigger()
        {
            if (_fn != NULL)
                ConnectionType<RETURN_TYPE>::value.set(_fn(arg1.get(), arg2.get()));
        }
        
        virtual Connection* clone() const
        {
            return new Connection2(*this);
        }


    };

// Specialization of Connection2 for void
    template<class ARG1_TYPE, class ARG2_TYPE>
    class Connection2 <void, ARG1_TYPE, ARG2_TYPE>: public ConnectionType<void>
    {
    private:
        void (*_fn)(ARG1_TYPE, ARG2_TYPE);
        LinkVar<ARG1_TYPE> arg1;
        LinkVar<ARG2_TYPE> arg2;

    public:
        Connection2()
                : ConnectionType<void>()
                , _fn(NULL)
        {}
        Connection2(void (*FN)(ARG1_TYPE, ARG2_TYPE), const LinkVar<ARG1_TYPE>& arg1, const LinkVar<ARG2_TYPE>& arg2)
                : ConnectionType<void>()
                , _fn(FN)
                , arg1(arg1)
                , arg2(arg2)
        {}
        virtual ~Connection2()
        {}

        void connect(void (*FN)(ARG1_TYPE, ARG2_TYPE))
        {
            _fn = FN;
        }

        void setArg1(const ARG1_TYPE& value)
        {
            arg1.set(value);
        }
        void linkArg1(ARG1_TYPE* value_ptr)
        {
            arg1.link(value_ptr);
        }
        ARG1_TYPE getArg1()
        {
            return arg1.get();
        }
        void setArg2(const ARG2_TYPE& value)
        {
            arg2.set(value);
        }
        void linkArg2(ARG2_TYPE* value_ptr)
        {
            arg2.link(value_ptr);
        }
        ARG2_TYPE getArg2()
        {
            return arg2.get();
        }
        
        void setArgs(const ARG1_TYPE& val1, const ARG2_TYPE& val2)
        {
            arg1.set(val1);
            arg2.set(val2);
        }
        void setArgs(ARG1_TYPE* val1, const ARG2_TYPE& val2)
        {
            arg1.link(val1);
            arg2.set(val2);
        }
        void setArgs(const ARG1_TYPE& val1, ARG2_TYPE* val2)
        {
            arg1.set(val1);
            arg2.link(val2);
        }
        void setArgs(ARG1_TYPE* val1, ARG2_TYPE* val2)
        {
            arg1.link(val1);
            arg2.link(val2);
        }

        void trigger()
        {
            if (_fn != NULL)
                _fn(arg1.get(), arg2.get());
        }
        
        virtual Connection* clone() const
        {
            return new Connection2(*this);
        }


    };
    
    
// Connection for methods with 2 arguments

    template<class CLASS, class RETURN_TYPE, class ARG1_TYPE, class ARG2_TYPE>
    class MethodConnection2 : public ConnectionType<RETURN_TYPE>
    {
    private:
        CLASS* object;
        RETURN_TYPE (CLASS::*method)(ARG1_TYPE, ARG2_TYPE);
        LinkVar<ARG1_TYPE> arg1;
        LinkVar<ARG2_TYPE> arg2;

    public:
        MethodConnection2()
                : ConnectionType<RETURN_TYPE>()
                , object(NULL)
                , method(NULL)
        {}
        MethodConnection2(CLASS* object, RETURN_TYPE (CLASS::*method)(ARG1_TYPE, ARG2_TYPE), const LinkVar<ARG1_TYPE>& arg1, const LinkVar<ARG2_TYPE>& arg2)
                : ConnectionType<RETURN_TYPE>()
                , object(object)
                , method(method)
                , arg1(arg1)
                , arg2(arg2)
        {}
        virtual ~MethodConnection2()
        {}
        
        void connect(CLASS* object, RETURN_TYPE (CLASS::*method)(ARG1_TYPE, ARG2_TYPE))
        {
            this->object = object;
            this->method = method;
        }

        void trigger()
        {
            if (object != NULL)
                ConnectionType<RETURN_TYPE>::value.set((object->*method)(arg1.get(), arg2.get()));
        }
        
        virtual Connection* clone() const
        {
            return new MethodConnection2(*this);
        }
    };

// Specialization of MethodConnection2 for void

    template<class CLASS, class ARG1_TYPE, class ARG2_TYPE>
    class MethodConnection2 <CLASS, void, ARG1_TYPE, ARG2_TYPE> : public ConnectionType<void>
    {
    private:
        CLASS* object;
        void (CLASS::*method)(ARG1_TYPE, ARG2_TYPE);
        LinkVar<ARG1_TYPE> arg1;
        LinkVar<ARG2_TYPE> arg2;

    public:
        MethodConnection2()
                : ConnectionType<void>()
                , object(NULL)
                , method(NULL)
        {}
        MethodConnection2(CLASS* object, void (CLASS::*method)(ARG1_TYPE, ARG2_TYPE), const LinkVar<ARG1_TYPE>& arg1, const LinkVar<ARG2_TYPE>& arg2)
                : ConnectionType<void>()
                , object(object)
                , method(method)
                , arg1(arg1)
                , arg2(arg2)
        {}
        virtual ~MethodConnection2()
        {}
        
        void connect(CLASS* object, void (CLASS::*method)(ARG1_TYPE, ARG2_TYPE))
        {
            this->object = object;
            this->method = method;
        }

        void trigger()
        {
            if (object != NULL)
                (object->*method)(arg1.get(), arg2.get());
        }
        
        virtual Connection* clone() const
        {
            return new MethodConnection2(*this);
        }
    };


// Connection with 3 arguments

    template<class RETURN_TYPE, class ARG1_TYPE, class ARG2_TYPE, class ARG3_TYPE>
    class Connection3 : public ConnectionType<RETURN_TYPE>
    {
    private:
        RETURN_TYPE (*_fn)(ARG1_TYPE, ARG2_TYPE, ARG3_TYPE);
        LinkVar<ARG1_TYPE> arg1;
        LinkVar<ARG2_TYPE> arg2;
        LinkVar<ARG3_TYPE> arg3;

    public:
        Connection3()
                : ConnectionType<RETURN_TYPE>()
                , _fn(NULL)
        {}
        Connection3(RETURN_TYPE (*FN)(ARG1_TYPE, ARG2_TYPE, ARG3_TYPE), const LinkVar<ARG1_TYPE>& arg1, const LinkVar<ARG2_TYPE>& arg2, const LinkVar<ARG3_TYPE>& arg3)
                : ConnectionType<RETURN_TYPE>()
                , _fn(FN)
                , arg1(arg1)
                , arg2(arg2)
                , arg3(arg3)
        {}
        virtual ~Connection3()
        {}

        void setFn(RETURN_TYPE (*FN)(ARG1_TYPE, ARG2_TYPE, ARG3_TYPE))
        {
            _fn = FN;
        }
        RETURN_TYPE (*getFn())(ARG1_TYPE, ARG2_TYPE, ARG3_TYPE)
        {
            return _fn;
        }

        void setArg1(const ARG1_TYPE& value)
        {
            arg1.set(value);
        }
        void linkArg1(ARG1_TYPE* value_ptr)
        {
            arg1.link(value_ptr);
        }
        ARG1_TYPE getArg1()
        {
            return arg1.get();
        }
        void setArg2(const ARG2_TYPE& value)
        {
            arg2.set(value);
        }
        void linkArg2(ARG2_TYPE* value_ptr)
        {
            arg2.link(value_ptr);
        }
        ARG2_TYPE getArg2()
        {
            return arg2.get();
        }
        void setArg3(const ARG3_TYPE& value)
        {
            arg3.set(value);
        }
        void linkArg3(ARG3_TYPE* value_ptr)
        {
            arg3.link(value_ptr);
        }
        ARG3_TYPE getArg3()
        {
            return arg3.get();
        }

        void trigger()
        {
            if (_fn != NULL)
                ConnectionType<RETURN_TYPE>::value.set(_fn(arg1.get(), arg2.get(), arg3.get()));
        }
        
        virtual Connection* clone() const
        {
            return new Connection3(*this);
        }


    };

// Specialization of Connection3 for void
    template<class ARG1_TYPE, class ARG2_TYPE, class ARG3_TYPE>
    class Connection3 <void, ARG1_TYPE, ARG2_TYPE, ARG3_TYPE>: public ConnectionType<void>
    {
    private:
        void (*_fn)(ARG1_TYPE, ARG2_TYPE, ARG3_TYPE);
        LinkVar<ARG1_TYPE> arg1;
        LinkVar<ARG2_TYPE> arg2;
        LinkVar<ARG3_TYPE> arg3;

    public:
        Connection3()
                : ConnectionType<void>()
                , _fn(NULL)
        {}
        Connection3(void (*FN)(ARG1_TYPE, ARG2_TYPE, ARG3_TYPE), const LinkVar<ARG1_TYPE>& arg1, const LinkVar<ARG2_TYPE>& arg2, const LinkVar<ARG3_TYPE>& arg3)
                : ConnectionType<void>()
                , _fn(FN)
                , arg1(arg1)
                , arg2(arg2)
                , arg3(arg3)
        {}
        virtual ~Connection3()
        {}

        void setFn(void (*FN)(ARG1_TYPE, ARG2_TYPE, ARG3_TYPE))
        {
            _fn = FN;
        }
        void (*getFn())(ARG1_TYPE, ARG2_TYPE, ARG3_TYPE)
        {
            return _fn;
        }

        void setArg1(const ARG1_TYPE& value)
        {
            arg1.set(value);
        }
        void linkArg1(ARG1_TYPE* value_ptr)
        {
            arg1.link(value_ptr);
        }
        ARG1_TYPE getArg1()
        {
            return arg1.get();
        }
        void setArg2(const ARG2_TYPE& value)
        {
            arg2.set(value);
        }
        void linkArg2(ARG2_TYPE* value_ptr)
        {
            arg2.link(value_ptr);
        }
        ARG2_TYPE getArg2()
        {
            return arg2.get();
        }
        void setArg3(const ARG3_TYPE& value)
        {
            arg3.set(value);
        }
        void linkArg3(ARG3_TYPE* value_ptr)
        {
            arg3.link(value_ptr);
        }
        ARG3_TYPE getArg3()
        {
            return arg3.get();
        }

        void trigger()
        {
            if (_fn != NULL)
                _fn(arg1.get(), arg2.get(), arg3.get());
        }
        
        virtual Connection* clone() const
        {
            return new Connection3(*this);
        }


    };
    
    
// Connection for methods with 3 arguments

    template<class CLASS, class RETURN_TYPE, class ARG1_TYPE, class ARG2_TYPE, class ARG3_TYPE>
    class MethodConnection3 : public ConnectionType<RETURN_TYPE>
    {
    private:
        CLASS* object;
        RETURN_TYPE (CLASS::*method)(ARG1_TYPE, ARG2_TYPE, ARG3_TYPE);
        LinkVar<ARG1_TYPE> arg1;
        LinkVar<ARG2_TYPE> arg2;
        LinkVar<ARG3_TYPE> arg3;

    public:
        MethodConnection3()
                : ConnectionType<RETURN_TYPE>()
                , object(NULL)
                , method(NULL)
        {}
        MethodConnection3(CLASS* object, RETURN_TYPE (CLASS::*method)(ARG1_TYPE, ARG2_TYPE, ARG3_TYPE), const LinkVar<ARG1_TYPE>& arg1, const LinkVar<ARG2_TYPE>& arg2, const LinkVar<ARG3_TYPE>& arg3)
                : ConnectionType<RETURN_TYPE>()
                , object(object)
                , method(method)
                , arg1(arg1)
                , arg2(arg2)
                , arg3(arg3)
        {}
        virtual ~MethodConnection3()
        {}
        
        void connect(CLASS* object, RETURN_TYPE (CLASS::*method)(ARG1_TYPE, ARG2_TYPE, ARG3_TYPE))
        {
            this->object = object;
            this->method = method;
        }

        void trigger()
        {
            if(object != NULL)
                ConnectionType<RETURN_TYPE>::value.set((object->*method)(arg1.get(), arg2.get(), arg3.get()));
        }
        
        virtual Connection* clone() const
        {
            return new MethodConnection3(*this);
        }
    };

// Specialization of MethodConnection3 for void

    template<class CLASS, class ARG1_TYPE, class ARG2_TYPE, class ARG3_TYPE>
    class MethodConnection3 <CLASS, void, ARG1_TYPE, ARG2_TYPE, ARG3_TYPE> : public ConnectionType<void>
    {
    private:
        CLASS* object;
        void (CLASS::*method)(ARG1_TYPE, ARG2_TYPE, ARG3_TYPE);
        LinkVar<ARG1_TYPE> arg1;
        LinkVar<ARG2_TYPE> arg2;
        LinkVar<ARG3_TYPE> arg3;

    public:
        MethodConnection3()
                : ConnectionType<void>()
                , object(NULL)
                , method(NULL)
        {}
        MethodConnection3(CLASS* object, void (CLASS::*method)(ARG1_TYPE, ARG2_TYPE, ARG3_TYPE), const LinkVar<ARG1_TYPE>& arg1, const LinkVar<ARG2_TYPE>& arg2, const LinkVar<ARG3_TYPE>& arg3)
                : ConnectionType<void>()
                , object(object)
                , method(method)
                , arg1(arg1)
                , arg2(arg2)
                , arg3(arg3)
        {}
        virtual ~MethodConnection3()
        {}
        
        void connect(CLASS* object, void (CLASS::*method)(ARG1_TYPE, ARG2_TYPE, ARG3_TYPE))
        {
            this->object = object;
            this->method = method;
        }

        void trigger()
        {
            if (object != NULL)
                (object->*method)(arg1.get(), arg2.get(), arg3.get());
        }
        
        virtual Connection* clone() const
        {
            return new MethodConnection3(*this);
        }
    };
    
    
    
    
// Widgets use this to perform callbacks based on events
// TODO: Make this multiple templates which have a member variable of each template type
    class BeaconObj
    {
        friend class Connection;
        static int connectionID;
        
        
    public:
        set<shared_ptr<Connection>, CompareConnections> connections;
        
        BeaconObj();
        BeaconObj(const BeaconObj& beacon);
        ~BeaconObj();
        BeaconObj& operator=(const BeaconObj& beacon);
        
        
        void emit();
        
        // Add a pre-prepared connection
        void addConnection(Connection* c);
        void addConnection(const shared_ptr<Connection>& c);
        
        
        shared_ptr<Connection> getConnection(int connectionID);
        shared_ptr<Connection> disconnect(int connectionID);
        
        void disconnect(Connection* connection);
        
        void deleteConnections();
        
        
        
		
        template<typename RETURN_TYPE>
        Connection* connect(RETURN_TYPE (*fn)())
		{
			Connection0<RETURN_TYPE>* c = new Connection0<RETURN_TYPE>(fn);
            
			addConnection(c);
			return c;
		}
        template<typename RETURN_TYPE, typename ARG1_TYPE>
        Connection* connect(RETURN_TYPE (*fn)(ARG1_TYPE), const LinkVar<ARG1_TYPE>& arg1)
		{
			Connection1<RETURN_TYPE, ARG1_TYPE>* c = new Connection1<RETURN_TYPE, ARG1_TYPE>(fn, arg1);
			
			addConnection(c);
			return c;
		}
		
        template<typename RETURN_TYPE, typename ARG1_TYPE, typename ARG2_TYPE>
        Connection* connect(RETURN_TYPE (*fn)(ARG1_TYPE, ARG2_TYPE), const LinkVar<ARG1_TYPE>& arg1, const LinkVar<ARG2_TYPE>& arg2)
		{
			Connection2<RETURN_TYPE, ARG1_TYPE, ARG2_TYPE>* c = new Connection2<RETURN_TYPE, ARG1_TYPE, ARG2_TYPE>(fn, arg1, arg2);
			
			addConnection(c);
			return c;
		}
        
        template<typename RETURN_TYPE, typename ARG1_TYPE, typename ARG2_TYPE, typename ARG3_TYPE>
        Connection* connect(RETURN_TYPE (*fn)(ARG1_TYPE, ARG2_TYPE, ARG3_TYPE), const LinkVar<ARG1_TYPE>& arg1, const LinkVar<ARG2_TYPE>& arg2, const LinkVar<ARG3_TYPE>& arg3)
		{
			Connection3<RETURN_TYPE, ARG1_TYPE, ARG2_TYPE, ARG3_TYPE>* c = new Connection3<RETURN_TYPE, ARG1_TYPE, ARG2_TYPE, ARG3_TYPE>(fn, arg1, arg2, arg3);
			
			addConnection(c);
			return c;
		}
		
		
		
		
        // Method Connections
        
        template<typename CLASS, typename RETURN_TYPE>
        Connection* connect(CLASS* object, RETURN_TYPE (CLASS::*method)())
		{
			MethodConnection0<CLASS, RETURN_TYPE>* c = new MethodConnection0<CLASS, RETURN_TYPE>(object, method);
			
			addConnection(c);
			Observer* o = dynamic_cast<Observer*>(object);
			if(o != NULL)
                o->connect(c);
			return c;
		}
		
        template<typename CLASS, typename RETURN_TYPE, typename ARG1_TYPE>
        Connection* connect(CLASS* object, RETURN_TYPE (CLASS::*method)(ARG1_TYPE), const LinkVar<ARG1_TYPE>& arg1)
		{
			MethodConnection1<CLASS, RETURN_TYPE, ARG1_TYPE>* c = new MethodConnection1<CLASS, RETURN_TYPE, ARG1_TYPE>(object, method, arg1);
			
			addConnection(c);
			Observer* o = dynamic_cast<Observer*>(object);
			if(o != NULL)
                o->connect(c);
			return c;
		}
		
        template<typename CLASS, typename RETURN_TYPE, typename ARG1_TYPE, typename ARG2_TYPE>
        Connection* connect(CLASS* object, RETURN_TYPE (CLASS::*method)(ARG1_TYPE, ARG2_TYPE), const LinkVar<ARG1_TYPE>& arg1, const LinkVar<ARG2_TYPE>& arg2)
		{
			MethodConnection2<CLASS, RETURN_TYPE, ARG1_TYPE, ARG2_TYPE>* c = new MethodConnection2<CLASS, RETURN_TYPE, ARG1_TYPE, ARG2_TYPE>(object, method, arg1, arg2);
			
			addConnection(c);
			Observer* o = dynamic_cast<Observer*>(object);
			if(o != NULL)
                o->connect(c);
			return c;
		}
		
		
    };
    
    class Beacon
    {
        public:
        shared_ptr<BeaconObj> obj;
        
        Beacon();
        Beacon(const Beacon& beacon);
        Beacon& operator=(const Beacon& beacon);
        
        void emit();
		
		
		
        template<typename RETURN_TYPE>
        Connection* connect(RETURN_TYPE (*fn)())
		{
			return obj->connect<RETURN_TYPE>(fn);
		}
        template<typename RETURN_TYPE, typename ARG1_TYPE>
        Connection* connect(RETURN_TYPE (*fn)(ARG1_TYPE), const LinkVar<ARG1_TYPE>& arg1)
		{
			return obj->connect<RETURN_TYPE, ARG1_TYPE>(fn, arg1);
		}
		
        template<typename RETURN_TYPE, typename ARG1_TYPE, typename ARG2_TYPE>
        Connection* connect(RETURN_TYPE (*fn)(ARG1_TYPE, ARG2_TYPE), const LinkVar<ARG1_TYPE>& arg1, const LinkVar<ARG2_TYPE>& arg2)
		{
			return obj->connect<RETURN_TYPE, ARG1_TYPE, ARG2_TYPE>(fn, arg1, arg2);
		}
        
        template<typename RETURN_TYPE, typename ARG1_TYPE, typename ARG2_TYPE, typename ARG3_TYPE>
        Connection* connect(RETURN_TYPE (*fn)(ARG1_TYPE, ARG2_TYPE, ARG3_TYPE), const LinkVar<ARG1_TYPE>& arg1, const LinkVar<ARG2_TYPE>& arg2, const LinkVar<ARG3_TYPE>& arg3)
		{
			return obj->connect<RETURN_TYPE, ARG1_TYPE, ARG2_TYPE, ARG3_TYPE>(fn, arg1, arg2, arg3);
		}
		
		
		
		
        // Method Connections
        
        template<typename CLASS, typename RETURN_TYPE>
        Connection* connect(CLASS* object, RETURN_TYPE (CLASS::*method)())
		{
			return obj->connect<CLASS, RETURN_TYPE>(object, method);
		}
		
        template<typename CLASS, typename RETURN_TYPE, typename ARG1_TYPE>
        Connection* connect(CLASS* object, RETURN_TYPE (CLASS::*method)(ARG1_TYPE), const LinkVar<ARG1_TYPE>& arg1)
		{
			return obj->connect<CLASS, RETURN_TYPE, ARG1_TYPE>(object, method, arg1);
		}
		
        template<typename CLASS, typename RETURN_TYPE, typename ARG1_TYPE, typename ARG2_TYPE>
        Connection* connect(CLASS* object, RETURN_TYPE (CLASS::*method)(ARG1_TYPE, ARG2_TYPE), const LinkVar<ARG1_TYPE>& arg1, const LinkVar<ARG2_TYPE>& arg2)
		{
			return obj->connect<CLASS, RETURN_TYPE, ARG1_TYPE, ARG2_TYPE>(object, method, arg1, arg2);
		}
    };

    
}  // END namespace GRO
    

#endif

